import { arrayEach } from '../../../helpers/array';
import { cellCoordFactory, isFormulaExpression } from '../utils';
import CellValue from '../cell/value';
import ExpressionModifier from '../expressionModifier';

/**
 * When "insert_row" is triggered the following operations must be performed:
 *
 * - All formulas which contain cell coordinates must be updated and saved into source data - Row must be increased
 *   by "amount" of times (eq: D4 to D5, $F$5 to $F$6);
 * - Mark all formulas which need update with "STATE_OUT_OFF_DATE" flag, so they can be recalculated after the operation.
 */
export const OPERATION_NAME = 'insert_row';

/**
 * Execute changes.
 *
 * @param {number} start Index row from which the operation starts.
 * @param {number} amount Count of rows to be inserted.
 * @param {boolean} [modifyFormula=true] If `true` all formula expressions will be modified according to the changes.
 *                                       `false` value is used by UndoRedo plugin which saves snapshoots before alter
 *                                       operation so it doesn't modify formulas if undo action is triggered.
 */
export function operate(start, amount, modifyFormula = true) {
  const { matrix, dataProvider } = this;
  const translate = [amount, 0];

  arrayEach(matrix.cellReferences, (cell) => {
    if (cell.row >= start) {
      cell.translateTo(...translate);
    }
  });

  arrayEach(matrix.data, (cell) => {
    const { row: origRow, column: origColumn } = cell;

    if (cell.row >= start) {
      cell.translateTo(...translate);
      cell.setState(CellValue.STATE_OUT_OFF_DATE);
    }

    if (modifyFormula) {
      const { row, column } = cell;
      const value = dataProvider.getSourceDataAtCell(row, column);

      if (isFormulaExpression(value)) {
        const startCoord = cellCoordFactory('row', start);
        const expModifier = new ExpressionModifier(value);

        expModifier.useCustomModifier(customTranslateModifier);
        expModifier.translate({ row: amount }, startCoord({ row: origRow, column: origColumn }));

        dataProvider.updateSourceData(row, column, expModifier.toString());
      }
    }
  });
}

/**
 * @param {cellCoord} cell The cell coordinates.
 * @param {string} axis The axis defined as "row" or "column".
 * @param {number} delta The shift/delta betwen old and new position.
 * @param {number} startFromIndex The index from the operation was performed.
 * @returns {Array}
 */
function customTranslateModifier(cell, axis, delta, startFromIndex) {
  const { start, end } = cell;
  const startIndex = start[axis].index;
  const endIndex = end[axis].index;
  let deltaStart = delta;
  let deltaEnd = delta;

  // Do not translate cells above inserted row or on the left of inserted column
  if (startFromIndex > startIndex) {
    deltaStart = 0;
  }
  if (startFromIndex > endIndex) {
    deltaEnd = 0;
  }

  return [deltaStart, deltaEnd, false];
}
